/*
 *  * Copyright (C) 2021 Huawei Device Co., Ltd.
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

package com.viewpagerindicator;

import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.PageSlider;
import ohos.agp.components.TabList;
import ohos.agp.components.element.Element;
import ohos.agp.database.DataSetSubscriber;
import ohos.agp.utils.Color;
import ohos.agp.utils.TextAlignment;
import ohos.agp.window.service.DisplayAttributes;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;

import com.viewpagerindicator.util.LogUtil;


/**
 * The type Pager sliding tab.
 */
public class PagerSlidingTab extends TabList implements Component.BindStateChangedListener {
    private static final int INTERVAL_POSITION = 2;
    private final PagerAdapterObserver mAdapterObserver = new PagerAdapterObserver();
    private final PageListener mPageListener = new PageListener();
    private PageSlider.PageChangedListener mDelegatePageListener;
    private CommonTabSelectedListener mCommonTabSelectedListener = new CommonTabSelectedListener();
    private int mCurrentPosition;
    private PageSlider mPager;
    private Element mTabBackgroundElement;
    private int mTabCount;
    private OnTabReselectedListener mTabReselectedListener = null;
    private int mIndicatorHeight = 10;
    private int mIndicatorColor = Color.CYAN.getValue();
    private int mTabPadding = 80;
    private int mTabTextSize = 40;
    private float mScreenWidth;
    private int mLastPosition = -1;
    private boolean mIsTextShown = true;

    /**
     * Instantiates a new Pager sliding tab.
     *
     * @param context the context
     */
    public PagerSlidingTab(Context context) {
        this(context, null);
    }

    /**
     * Instantiates a new Pager sliding tab.
     *
     * @param context the context
     * @param attrSet the attr set
     */
    public PagerSlidingTab(Context context, AttrSet attrSet) {
        this(context, attrSet, null);
    }

    /**
     * Instantiates a new Pager sliding tab.
     *
     * @param context   the context
     * @param attrSet   the attr set
     * @param styleName the style name
     */
    public PagerSlidingTab(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        setOrientation(ComponentContainer.HORIZONTAL);
        setIndicatorType(TabList.INDICATOR_BOTTOM_LINE);
        setTabTextAlignment(TextAlignment.VERTICAL_CENTER);
        addTabSelectedListener(mCommonTabSelectedListener);

        setTabMargin(mTabPadding);
        setTabTextSize(mTabTextSize);
        setTabTextColors(0xffffffff,
                0xff3f51b5);
        setSelectedTabIndicatorColor(mIndicatorColor);
        setSelectedTabIndicatorHeight(mIndicatorHeight);
        setBindStateChangedListener(this);
        DisplayAttributes displayAttributes = DisplayManager.getInstance().getDefaultDisplay
                (getContext()).get().getAttributes();
        mScreenWidth = displayAttributes.width;
    }

    /**
     * Sets text color.
     *
     * @param color      the color
     * @param pressColor the press color
     */
    public void setTextColor(int color, int pressColor) {
        setTabTextColors(color, pressColor);
    }

    /**
     * Gets tab background element.
     *
     * @return the tab background element
     */
    public Element getTabBackgroundElement() {
        return mTabBackgroundElement;
    }

    /**
     * Sets tab background element.
     *
     * @param tabBackgroundElement the tab background element
     */
    public void setTabBackgroundElement(Element tabBackgroundElement) {
        this.mTabBackgroundElement = tabBackgroundElement;
    }

    /**
     * Gets text size.
     *
     * @return the text size
     */
    public int getTextSize() {
        return mTabTextSize;
    }

    /**
     * Sets text size.
     *
     * @param textSizePx the text size px
     */
    public void setTextSize(int textSizePx) {
        this.mTabTextSize = textSizePx;
        setTabTextSize(mTabTextSize);
        invalidate();
    }

    /**
     * Gets indicator height.
     *
     * @return the indicator height
     */
    public int getIndicatorHeight() {
        return mIndicatorHeight;
    }

    /**
     * Sets indicator height.
     *
     * @param indicatorLineHeightPx the indicator line height px
     */
    public void setIndicatorHeight(int indicatorLineHeightPx) {
        this.mIndicatorHeight = indicatorLineHeightPx;
        setSelectedTabIndicatorHeight(mIndicatorHeight);
        invalidate();
    }

    /**
     * Gets indicator color.
     *
     * @return the indicator color
     */
    public int getIndicatorColor() {
        return this.mIndicatorColor;
    }

    /**
     * Sets indicator color.
     *
     * @param indicatorColor the indicator color
     */
    public void setIndicatorColor(int indicatorColor) {
        this.mIndicatorColor = indicatorColor;
        setSelectedTabIndicatorColor(indicatorColor);
        invalidate();
    }

    /**
     * Gets tab padding left right.
     *
     * @return the tab padding left right
     */
    public int getTabPaddingLeftRight() {
        return mTabPadding;
    }

    /**
     * Sets tab padding left right.
     *
     * @param paddingPx the padding px
     */
    public void setTabPaddingLeftRight(int paddingPx) {
        this.mTabPadding = paddingPx;
        setTabMargin(paddingPx);
        invalidate();
    }

    /**
     * gets Tab Count
     *
     * @return int
     */
    public int getTabCount() {
        return mTabCount;
    }

    /**
     * Gets current position.
     *
     * @return the current position
     */
    public int getCurrentPosition() {
        return mCurrentPosition;
    }


    /**
     * Set is text shown.
     *
     * @param isTextShown the is text shown
     */
    public void setIsTextShown(boolean isTextShown) {
        mIsTextShown = isTextShown;
    }

    /**
     * Sets view pager.
     *
     * @param pager the pager
     */
    public void setViewPager(PageSlider pager) {
        this.mPager = pager;
        if (pager.getProvider() == null) {
            throw new IllegalStateException("ViewPager does not have adapter instance.");
        }

        pager.addPageChangedListener(mPageListener);
        pager.getProvider().addDataSubscriber(mAdapterObserver);
        mAdapterObserver.setAttached(true);
        notifyDataSetChanged();
    }


    /**
     * Notify data set changed.
     */
    public void notifyDataSetChanged() {
        removeAllComponents();
        mTabCount = mPager.getProvider().getCount();
        for (int i = 0; i < mTabCount; i++) {
            Tab tab = new Tab(getContext());
            addTab(i, tab);
        }
        invalidate();
    }


    private void addTab(int position, Tab tab) {
        if (tab == null) {
            return;
        }
        String title = mPager.getProvider().getPageTitle(position);
        if (title != null) {
            if (mIsTextShown) {
                tab.setText(title);
            }
        }
        if (mTabBackgroundElement != null) {
            tab.setBackground(mTabBackgroundElement);
        }

        addTab(tab);
    }


    /**
     * Sets on tab reselected listener.
     *
     * @param tabReselectedListener the tab reselected listener
     */
    public void setOnTabReselectedListener(OnTabReselectedListener tabReselectedListener) {
        this.mTabReselectedListener = tabReselectedListener;
    }

    /**
     * Sets on page change listener.
     *
     * @param listener the listener
     */
    public void setOnPageChangeListener(PageSlider.PageChangedListener listener) {
        this.mDelegatePageListener = listener;
    }

    @Override
    public void onComponentBoundToWindow(Component component) {
        if (mPager != null) {
            if (!mAdapterObserver.isAttached()) {
                mPager.getProvider().removeDataSubscriber(mAdapterObserver);
                mAdapterObserver.setAttached(true);
            }
        }
    }

    @Override
    public void onComponentUnboundFromWindow(Component component) {
        if (mPager != null) {
            if (mAdapterObserver.isAttached()) {
                mPager.getProvider().removeDataSubscriber(mAdapterObserver);
                mAdapterObserver.setAttached(false);
            }
        }
    }

    private void autoScroll(int position) {
        int count = getTabCount();
        float scrollX;
        int advancePosition;

        if (position >= mLastPosition) {
            if (position >= (count - 1) - INTERVAL_POSITION) {
                advancePosition = count - 1;
            } else {
                advancePosition = position + INTERVAL_POSITION;
            }

            int advanceX = getTabAt(advancePosition).getLocationOnScreen()[0];
            int advanceWidth = getTabAt(advancePosition).getPaddingLeft()
                    + getTabAt(advancePosition).getWidth() + getTabAt(advancePosition).getPaddingRight() + mTabPadding;

            if (advanceX + advanceWidth > mScreenWidth) {
                scrollX = advanceX + advanceWidth - mScreenWidth;
            } else {
                scrollX = 0;
            }
        } else {
            if (position <= INTERVAL_POSITION) {
                advancePosition = 0;
            } else {
                advancePosition = position - INTERVAL_POSITION;
            }

            int advanceX = getTabAt(advancePosition).getLocationOnScreen()[0];
            scrollX = Math.min(0, advanceX - mTabPadding);
        }
        this.mLastPosition = position;

        if (scrollX == 0) {
            return;
        }
        fluentScrollByX((int) scrollX);
    }


    /**
     * The interface On tab reselected listener.
     */
    public interface OnTabReselectedListener {
        /**
         * On tab reselected.
         *
         * @param position the position
         */
        void onTabReselected(int position);
    }

    private class CommonTabSelectedListener implements TabSelectedListener {
        @Override
        public void onSelected(Tab tab) {
            if (tab == null) {
                return;
            }
            int position = tab.getPosition();
            mPager.setCurrentPage(position);
            if (mTabReselectedListener != null) {
                mTabReselectedListener.onTabReselected(position);
            }
            autoScroll(position);
        }

        @Override
        public void onUnselected(Tab tab) {
        }

        @Override
        public void onReselected(Tab tab) {
        }
    }

    private class PageListener implements PageSlider.PageChangedListener {
        @Override
        public void onPageSliding(int position, float positionOffset, int positionOffsetPixels) {
            mCurrentPosition = position;
            float posOffset = positionOffset;
            if (positionOffset == 1f) {
                posOffset = 0f;
            }

            if (mDelegatePageListener != null) {
                mDelegatePageListener.onPageSliding(position, posOffset, positionOffsetPixels);
            }
        }

        @Override
        public void onPageSlideStateChanged(int state) {
            if (mDelegatePageListener != null) {
                mDelegatePageListener.onPageSlideStateChanged(state);
            }
        }

        @Override
        public void onPageChosen(int position) {
            selectTabAt(position);
            if (mDelegatePageListener != null) {
                mDelegatePageListener.onPageChosen(position);
            }
        }

    }

    private class PagerAdapterObserver extends DataSetSubscriber {
        private boolean attached = false;

        @Override
        public void onChanged() {
            notifyDataSetChanged();
        }

        /**
         * Is attached boolean.
         *
         * @return the boolean
         */
        boolean isAttached() {
            return attached;
        }

        /**
         * Sets attached.
         *
         * @param attached the attached
         */
        void setAttached(boolean attached) {
            this.attached = attached;
        }
    }
}
